package org.threebitsoftware.tbb.dao.bridge;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Vector;

import org.apache.log4j.Logger;
import org.threebitsoftware.tbb.dao.Contact;
import org.threebitsoftware.tbb.dao.DaoHelper;
import org.threebitsoftware.tbb.dao.PhoneNumber;
import org.threebitsoftware.dao.generated.ContactKey;
import org.threebitsoftware.dao.generated.Phonenumber;
import org.threebitsoftware.dao.generated.PhonenumberKey;
import org.threebitsoftware.dao.generated.dao.ContactDAO;
import org.threebitsoftware.dao.generated.dao.PhonenumberDAO;
import org.threebitsoftware.dao.generated.orm.ContactDAOImpl;
import org.threebitsoftware.dao.generated.orm.PhonenumberDAOImpl;

import com.pew.util.dao.DatabaseConection;
import com.pew.util.dao.DatabaseUtil;

/*
 * Created: Nov 14, 2009
 * Original Author: Elwin
 * Project:
 * Copyright:
 * 
 * Purpose:
 */

/*
 * Copyright (c) 2009, 3BitSoftware
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of the <organization> nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

public class ContactBridge {
	private static Logger cLogger = Logger.getLogger(ContactBridge.class);
	
	/**
	 * Stores a Contact into the storage medium.
	 * Considers deletion as an act of storage; thus will
	 * delete an object if the object is marked for deletion.
	 * @param myPN ; The phone number to be stored.
	 */
	public static void storeContact(Contact myContact) 
	{
		ContactDAOImpl lDAO = new ContactDAOImpl();
		
		try {
		
			if(myContact.isMarkedForDeletion())
			{
				cLogger.debug("Found delete for: " + myContact.toString());
				if(myContact.getIdContact() > -1)
				{
					ContactKey lKey = new ContactKey();
					lKey.setIdContact(new Integer(myContact.getIdContact()).longValue());
					lDAO.delete(lKey, DaoHelper.getDBConnection());
				}
				else
				{
					cLogger.info("Nothting to do for Contact:" + myContact.toString() + "; it is not in the database based; decesion on no ID.");
				}
			}
			else
			{
				create(lDAO, myContact);
			}
		}
		catch(SQLException sqlex)
		{
			cLogger.error("An exception occured while trying to perform an operation to: " + myContact.toString());
			cLogger.error(sqlex.toString());
		}
	}
	
	/**
	 * Stores multiple Contact objects into the storage medium.
	 * @param myContact ; An array of Contact objects.
	 */
	public static void storeContact(Contact myContact[])
	{
		cLogger.debug("Entered storeContact(myContact[])");
		
		int liSize = myContact.length;
		for(int i = 0; i < liSize; i++) {
			storeContact(myContact[i]);
		}
		
		cLogger.info("Worked on storing " + liSize + " phone numbers.");
		
	}
	
	/**
	 * Retrieves a Contact object based on the unique
	 * id passed into it.
	 * @param myId ; A unique identifier for a Contact
	 * @return A Contact object; null if none exists or an error occurs.
	 */
	public static Contact getContact(int myId)
	{
		Contact lDomainObject = null;
		org.threebitsoftware.dao.generated.Contact lDaoObject = null;
		
		cLogger.debug("Getting a DAO object for id: " + myId);
		
		ContactDAOImpl lDAO = new ContactDAOImpl();
		ContactKey lKey = new ContactKey();
		lKey.setIdContact((new Integer(myId)).longValue());
		
		try {
			lDaoObject = lDAO.load(lKey, DaoHelper.getDBConnection());
		}
		catch(SQLException sqlex)
		{
			cLogger.error("Error retrieving phone number with id:" + myId, sqlex);
		}
		
		lDomainObject = mapDaoToDomain(lDaoObject);
		
		return lDomainObject;
	}
	
	/**
	 * Retrieves multiple Contact objects based on a 
	 * SQL-92 based conditional clause.
	 * @return An array of Contact objects; null if an error occurs.
	 */
	public static Contact[] getContacts(String myQuery)
	{
		cLogger.debug("Attempting to gather multiple domain objects based" +
				" on id query: " + myQuery);
		
		ArrayList<Contact> lDomainListColl = new ArrayList<Contact>();
		Contact lDomainList[] = null;
		ResultSet lRS = null;
		
		lRS = DatabaseUtil.runSelectStatement(DaoHelper.getDBConnection(),
				"SELECT ID_CONTACT FROM CONTACT WHERE " + myQuery);
		int lTempId =  -1;
		int i = 0;
		
		try {
			cLogger.debug("The query returned:" + lRS.getFetchSize());

			while(lRS.next()) {
				lTempId = lRS.getInt(1);
				lDomainListColl.add(getContact(lTempId));
			}
		}
		catch (SQLException sqlex)
		{
			cLogger.error("Exception occured while getting contacts based on"
					+ " query: " + myQuery, sqlex);
		}
		
		lDomainList = new Contact[lDomainListColl.size()];
		int count = 0;
		for(Contact c : lDomainListColl) {
			lDomainList[count] = c;
			count++;
		}
		System.out.println("Array lDomainList: \t" + lDomainList.length + "\t\tlDomainListColl: \t" + lDomainListColl.size());
		return lDomainList;
	}
	
	/**
	 * Maps a domain object to a DAO object.
	 * @param myContact
	 * @return
	 */
	static org.threebitsoftware.dao.generated.Contact mapDomainToDao(Contact myContact)
	{
		cLogger.debug("Attempting to map a contact object.");
		cLogger.debug("Contact: " + myContact.toString());
		
		org.threebitsoftware.dao.generated.Contact lGenerated = new org.threebitsoftware.dao.generated.Contact();
		
		lGenerated.setIdAppUser(myContact.getIdAppUser());
		lGenerated.setIdContact(myContact.getIdContact());
		lGenerated.setIIsperson((myContact.isPerson()?1:0));
		lGenerated.setSCompany(myContact.getCompany());
		lGenerated.setSFirstname(myContact.getFirstName());
		lGenerated.setSLastname(myContact.getLastName());
		lGenerated.setSMiddlename(myContact.getMiddleName());
		lGenerated.setSPrefix(myContact.getPrefix());
		lGenerated.setSSuffix(myContact.getSuffix());
		lGenerated.setSTitle(myContact.getTitle());

		cLogger.debug("Generated Contact mapping resulted in: " + lGenerated.toString());
		
		return lGenerated;
	}
	
	/**
	 * Maps a DAO object to the domain object.
	 * @param myContact
	 * @return The domain object; null otherwise.
	 */
	static Contact mapDaoToDomain(org.threebitsoftware.dao.generated.Contact myObject)
	{
		cLogger.debug("Attempting to map a phone number.");
		
		Contact lDomain = new Contact();
		
		if(myObject != null)
		{
			lDomain.setIdAppUser((new Long(myObject.getIdAppUser()).intValue()));
			lDomain.setIdContact((new Long(myObject.getIdContact())).intValue());
			lDomain.setIsPerson((myObject.getIIsperson() == 0?false:true));
			lDomain.setCompany(myObject.getSCompany());
			lDomain.setFirstName(myObject.getSFirstname());
			lDomain.setMiddleName(myObject.getSMiddlename());
			lDomain.setLastName(myObject.getSLastname());
			lDomain.setPrefix(myObject.getSPrefix());
			lDomain.setSuffix(myObject.getSSuffix());
			lDomain.setTitle(myObject.getSTitle());
		}
		else if (cLogger.isDebugEnabled())
		{
			cLogger.debug("NULL value passed to the method.  NULL returned.");
		}
		
		return lDomain;
	}

	static int create(ContactDAO myDAO, Contact myDomain) throws SQLException
	{
		org.threebitsoftware.dao.generated.Contact lDomainGenerated = mapDomainToDao(myDomain);
		String lClassName = myDomain.getClass().getName();
		int liReturnId = -1;
		
		cLogger.debug("Attempting to create / update a " + lClassName);
		
		if(lDomainGenerated.getIdContact() > -1)
		{
			cLogger.debug("Attempt to Update Object: " + lClassName + " = " + myDomain.getIdContact());
			myDAO.update(lDomainGenerated, DaoHelper.getDBConnection());
			
			cLogger.info("Done updating phone number.");
		}
		else
		{
			cLogger.debug("New" + lClassName + " Detected; id is -1.");
			
			//Logic here assists in testing and other special cases.
			
			if(lDomainGenerated.getIdContact() == -1)
			{
				cLogger.debug("Current id is -1; getting a new id.");
				int liNewId = DaoHelper.getNextId("Contact", "ID_CONTACT", DaoHelper.getDBConnection());
				cLogger.debug("Got id: " + liNewId);
				
				lDomainGenerated.setIdContact(new Integer(liNewId).longValue());
			}
			else if(cLogger.isDebugEnabled())
			{
				cLogger.debug("Id is not -1; creating a new object with the current id.");
			}
			
			myDAO.create(lDomainGenerated, DaoHelper.getDBConnection());
			
			cLogger.info("Created a new" + lClassName);
		}
		
		liReturnId = (new Long(lDomainGenerated.getIdContact()).intValue());
		myDomain.setIdContact(liReturnId);
		cLogger.info("Id worked on " + liReturnId + " for " + lClassName);
		return liReturnId;
	}
	
	/**
	 * Removes a full contact from the database.
	 * this is done here to know which order is necessary.
	 * @param myObject
	 */
	public static void deleteContact(Contact myObject)
	{
		if(myObject == null || 
				(myObject != null && myObject.getIdContact() == -1))
		{
			//nothing to do.  The contact is the center of the domain objects,
			//if it does not exist or if it does not have a valid ID, then
			//none of the other parts of the contact should be there.
			cLogger.info("A contact is marked for deletion but does not exist.  Nothing to do.");
		}
		else
		{
			cLogger.fatal("ContactBridge.deleteContact not yet implemented");
		}
		
	}
	
	public static String[] getDistinctTags()
	{
		String lStrQuery = "SELECT DISTINCT S_TAG FROM CONTACT_TAG";
		String lStringReturn[] = null;
		try {
			ResultSet lRs = DatabaseUtil.runSelectStatement(DaoHelper.getDBConnection(), lStrQuery);
			Vector lVectStr = new Vector();
			int liSize = 0;
			if(lRs != null) {
				while(lRs.next())
				{
					lVectStr.add(new String(lRs.getString(1)));
					liSize++;
				}
				
				lStringReturn = new String[liSize];
				lStringReturn = (String[]) lVectStr.toArray(lStringReturn);
			}
		}
		catch(SQLException sqlex)
		{
			cLogger.error("Could not get the distinct tags.");
		}
		
		if(lStringReturn == null)
		{
			lStringReturn = new String[1];
			lStringReturn[0] = "Default";
		}
		
		return lStringReturn;
	}
}
